﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Reflection;
using System.ComponentModel;

namespace CoffeePotServer
{
    /// <summary>
    /// File: HTCPCP.cs
    /// Author: James Dibble 10009689
    /// Last Revision: 17/03/12
    /// SVN Address:
    /// http://code.google.com/p/cnos-coffeepot-server/source/browse/branches/DibbleBranch/CoffeePotServerLib/CoffeePotServerLib/HTCPCPServer.cs
    /// </summary>
    public class HTCPCPServer
    {
        #region Attributes
        private static List<Pot> _pots;
        private static List<Client_Handler> _clients;
        private static Socket _sktServer;
        private static Boolean _bolDebug = true;
        #endregion

        #region Constructor
        /// <summary>
        /// An HTCPCP Server.
        /// </summary>
        /// <param name="port">Port to listen on.</param>
        public HTCPCPServer(int port)
        {
            _pots = new List<Pot>();
            _clients = new List<Client_Handler>();
            //Set up an IPV4/TCP Stream socket.
            _sktServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //Allow any IP address to connect to the socket.
            _sktServer.Bind(new IPEndPoint(IPAddress.Any, port));
            //Allow up to 5 clients to queue to be dealt with.  Should be impossible to get to 5...
            _sktServer.Listen(5);
        }
        #endregion

        #region Properties
        /// <summary>
        /// The pots controlled by the Server.
        /// </summary>
        public static List<Pot> Pots
        {
            get { return _pots; }
        }

        /// <summary>
        /// Clients connected to the server.
        /// </summary>
        public static List<Client_Handler> Clients
        {
            get{ return _clients; }
        }

        /// <summary>
        /// Whether verbose output is on.
        /// </summary>
        public static Boolean DebugMode
        {
            get{ return _bolDebug; }
            set { _bolDebug = value; }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Start accepting clients.
        /// </summary>
        public void StartListening()
        {
            _sktServer.BeginAccept(new AsyncCallback(ClientConnected), null);
        }

        /// <summary>
        /// Handle a newly connected client.
        /// </summary>
        /// <param name="arClient"></param>
        private void ClientConnected(IAsyncResult arClient)
        {
            Client_Handler clNewClient = new Client_Handler(_sktServer.EndAccept(arClient));
            _clients.Add(clNewClient);
            new Thread(clNewClient.HandleClient).Start();
            _sktServer.BeginAccept(new AsyncCallback(ClientConnected), null);
            Console.WriteLine("[{0}] {1} Connected",
                              DateTime.Now.ToLongTimeString(),
                              clNewClient.Hostname);
        }

        /// <summary>
        /// Close down the server.
        /// </summary>
        public void StopListening()
        {
            _sktServer.Close();
        }

        /// <summary>
        /// Create a new pot to use on the server.
        /// </summary>
        public void AddNewPot()
        {
            _pots.Add(new Pot(_pots.Count + 1));
        }

        /// <summary>
        //  Get a specific pot on the server.
        /// </summary>
        /// <param name="id">The pot designator</param>
        /// <returns>A pot or null if not found.</returns>
        public static Pot FindPotByID(int id)
        {
            //Use LINQ to find the pot.
            var result = from thisPot in _pots where thisPot.Designator == id select thisPot;
            //Take the first pot.
            foreach (Pot thisRes in result) return thisRes;
            //Couldn't find the pot.
            return null;
        }

        //Copied from my generic library.
        /// <summary>
        /// Get the value of an enum.
        /// </summary>
        /// <param name="value">The enum to get the value of.</param>
        /// <returns>The "Description" attributed to the enum.</returns>
        public static string GetEnumDescription(Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());

            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(
                typeof(DescriptionAttribute),
                false);

            if (attributes != null &&
                attributes.Length > 0)
                return attributes[0].Description;
            else
                return value.ToString();
        }
        #endregion

        #region Enums
        /// <summary>
        /// Server specific errors.
        /// </summary>
        public enum StatusCodes
        {
            [Description("400 Bad Request")]
            BADREQUEST,
            [Description("404 Not Found")]
            NOTFOUND,
            [Description("405 Method Not Found")]
            INVALIDMETHOD
        }
        #endregion
    }
}